#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <ipms.h>
#include <pcd_api.h>
#include "ipms_console.h"

extern pthread_t resp_recv_thread;

int ConsoleReportRequest(u_int16 id, char *args)
{
    pcd_report_request(id);
    return 0;
}

int ConsoleSetAutoReportTimer(u_int16 id, char *args)
{
    pcd_set_autoreport_timer(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSelectReportType(u_int16 id, char *args)
{
    pcd_report_type_select(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSetTurnOnTimer(u_int16 id, char *args)
{
    pcd_set_turnon_timer(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSetTurnOffTimer(u_int16 id, char *args)
{
    pcd_set_turnoff_timer(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleRequestMAC(u_int16 id, char *args)
{
    pcd_mac_request(id);
    return 0;
}

int ConsoleRequestStatus(u_int16 id, char *args)
{
    pcd_status_request(id);
    return 0;
}

#if 0
int ConsoleSetMaxVolt(u_int16 id, char *args)
{
    pcd_set_max_volt(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSetMinVolt(u_int16 id, char *args)
{
    pcd_set_min_volt(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSetMaxCurr(u_int16 id, char *args)
{
    pcd_set_max_volt(id, (u_int16)atoi(args));
    return 0;
}

int ConsoleSetMinCurr(u_int16 id, char *args)
{
    pcd_set_max_volt(id, (u_int16)atoi(args));
    return 0;
}
#endif

int ConsolePrintHelp(u_int16 id, char *args)
{
    printf("PCD Commands:\n");
    printf(" [req] {ID}                  Request reports.\n");
    printf(" [art] {ID} {time}           Set auto report timer.\n");
    printf(" [sel] {ID} {selection}      Select report data types.\n");
    printf(" [stn] {ID} {time}           Set auto turn on timer.\n");
    printf(" [stf] {ID} {time}           Set auto turn off timer.\n");
    printf(" [mac] {ID}                  Request MAC address.\n");
    printf(" [sta] {ID}                  Request status.\n");
    printf(" [mxv] {ID} {volt}           Set maximum alert voltage.\n");
    printf(" [mnv] {ID} {volt}           Set minimum alert voltage.\n");
    printf(" [mxc] {ID} {curr}           Set maximum alert current.\n");
    printf(" [mnc] {ID} {curr}           Set minimum alert current.\n");

    printf("System Commands:\n");
    printf(" [help]                     Print this help menu.\n");
    printf(" [quit]                     Quit program.\n");

    return 0;
}

int ConsoleQuit(u_int16 id, char *args)
{
    pthread_kill(resp_recv_thread, SIGQUIT);
    exit(1);
    return 0;
}

struct ipms_cmd commands[] = {
    { CMD_TYPE_PCD, "req", 1, "Request reports", &ConsoleReportRequest, 0 },
    { CMD_TYPE_PCD, "art", 0, "Set auto report timer", &ConsoleSetAutoReportTimer, 0 },
    { CMD_TYPE_PCD, "sel", 0, "Select report data types", &ConsoleSelectReportType, 0 },
    { CMD_TYPE_PCD, "stn", 0, "Set auto turn on timer", &ConsoleSetTurnOnTimer, 0 },
    { CMD_TYPE_PCD, "stf", 0, "Set auto turn off timer", &ConsoleSetTurnOffTimer, 0 },
    { CMD_TYPE_PCD, "mac", 1, "Request MAC address", &ConsoleRequestMAC, 0 },
    { CMD_TYPE_PCD, "sta", 1, "Request status", &ConsoleRequestStatus, 0 },
#if 0
    { CMD_TYPE_PCD, "mxv", 0, "Set maximum alert voltage", &ConsoleSetMaxVolt, 0 },
    { CMD_TYPE_PCD, "mnv", 0, "Set minimum alert voltage", &ConsoleSetMinVolt, 0 },
    { CMD_TYPE_PCD, "mxc", 0, "Set maximum alert current", &ConsoleSetMaxCurr, 0 },
    { CMD_TYPE_PCD, "mnc", 0, "Set minimum alert current", &ConsoleSetMinCurr, 0 },
#endif
    { CMD_TYPE_SYS, "help", 1, "Print command help", &ConsolePrintHelp, 0 },
    { CMD_TYPE_SYS, "quit", 0, "Quit program", &ConsoleQuit, 0 },
};

void ConsoleJoinCallback(struct pcd_struct *PCDInfo)
{
    PRINT_INFO("[JOIN EVT][%s] ID=%d MAC=%s\n",
               PCDInfo->last_update, PCDInfo->id, PCDInfo->mac_addr);
}

void ConsoleReportCallback(struct pcd_struct *PCDInfo)
{
    PRINT_INFO("[REQ OK][%s] ID=%d Volt=%d Curr=%d InstantPower=%d AccumPower=%d\n",
               PCDInfo->last_update, PCDInfo->id,
               PCDInfo->reports.voltage, PCDInfo->reports.current,
               PCDInfo->reports.instant_power, PCDInfo->reports.accum_power);
}

void ConsoleMACCallback(struct pcd_struct *PCDInfo)
{
    PRINT_INFO("[MAC OK][%s] ID=%d MAC=%s\n",
               PCDInfo->last_update, PCDInfo->id, PCDInfo->mac_addr);
}

void ConsoleStatusCallback(struct pcd_struct *PCDInfo)
{
    PRINT_INFO("[STA OK][%s] ID=%d TimeLeft=%d Status=%d\n",
               PCDInfo->last_update, PCDInfo->id,
               PCDInfo->time_left, PCDInfo->status);
}

void ConsoleEventProcess(struct pcd_struct *PCDInfo, u_int8 EventType)
{
    if (!PCDInfo)
        return;

    switch (EventType) {
        case RCMD_EVENT:
            PRINT_INFO("[CHG EVT][%s] ID=%d Event=%d\n",
                       PCDInfo->last_update, PCDInfo->id, PCDInfo->event);
            break;
        case RCMD_AUTO_REPORT:
            PRINT_INFO("[AUTO REP][%s] ID=%d Volt=%d Curr=%d InstantPower=%d AccumPower=%d\n",
                       PCDInfo->last_update, PCDInfo->id,
                       PCDInfo->reports.voltage, PCDInfo->reports.current,
                       PCDInfo->reports.instant_power, PCDInfo->reports.accum_power);
            break;
    }
}

int ConsoleExecCmd(void *CmdString)
{
    struct ipms_cmd *p_command = NULL;
    char s_cmd[CMD_MAX_SZ];
    char s_id[CMD_MAX_SZ];
    char s_arg[CMD_MAX_SZ];
    char *token;
    u_int16 id = 0;
    int status = STATUS_OK;
    int cmd_idx;

    if (!CmdString)
        return STATUS_ERROR_ARG;

    /* PRINT_DEBUG("Command string: %s\n", (char *)CmdString); */

    bzero(s_cmd, CMD_MAX_SZ);
    bzero(s_id, CMD_MAX_SZ);
    bzero(s_arg, CMD_MAX_SZ);

    strncpy(s_cmd, CmdString, CMD_MAX_SZ);
    token = strtok(s_cmd, "\n");
    token = strtok(token, " ");
    if (token) {
        strncpy(s_cmd , token, strlen(token));
        token = strtok(NULL, " ");
        if (token) {
            strncpy(s_id, token, strlen(token));
            id = (u_int16)atoi(s_id);
            token = strtok(NULL, " ");
            if (token)
                strncpy(s_arg, token, strlen(token));
        }
    }
    else
        return STATUS_UNKNOWN_CMD;

    /* PRINT_DEBUG("cmd=%s ID=%s arg=%s\n", s_cmd, s_id, s_arg); */

    /* Get command callback */
    for (cmd_idx = 0; cmd_idx < sizeof(commands)/sizeof(commands[0]); cmd_idx++) {
        if (strncasecmp(s_cmd, commands[cmd_idx].command, CMD_STR_LEN) == 0) {
            p_command = &commands[cmd_idx];
            break;
        }
    }

    if (!p_command) {
        fprintf(stderr, "%s: Unrecognized command [%s].\n", __func__, s_cmd);
        return STATUS_UNKNOWN_CMD;
    }

    switch(p_command->type) {
        case CMD_TYPE_SYS:
            p_command->callback(id, s_arg);
            status = STATUS_OK;
            break;
        case CMD_TYPE_PCD:
            if (pcd_list_search_by_id(id)) {
                p_command->owner = id;
                status = p_command->callback(id, s_arg);
            }
            else
                status = STATUS_UNKNOWN_ADDR;
    }

    return status;
}
